#include "aerial\Game\GameScene\RoomScene.h"
// scene change
#include "aerial\Game\GameScene\MainMenuScene.h"
#include "aerial\Game\GameScene\BattleScene.h"

//guiobject
#include "aerial\Object\Text.h"
#include "aerial\Object\TextInput.h"
#include "aerial\Object\Button.h"
#include "aerial\Object\Panel.h"
#include "aerial\Object\DisplayBoard.h"

#include "aerial\Scene\Event.h"
#include "aerial\Core\Core.h"

RoomScene::RoomScene(const char* _userName)
	: userName(_userName), gameStart(false), entered(false)
{
	memset(boards, 0, sizeof(boards));
	ready = start = 0;
	frameWidth = frameHeight = 0;
	flashWidth = flashHeight = 0;
}

bool RoomScene::initialize()
{
	elmtCount = 2;
	menu = new Panel(hgeResMan, -200.f, 0.f, 200.f, 600.f);
	menu->SetTransfromationTween(0.f, 0.f, 0.25f);

	mainPanel = new Panel( hgeResMan, 210.f, -500.f, 550.f, 500.f );
	mainPanel->SetTransfromationTween(210.f, 40.f, 0.25f);

	mainPanel->placeGuiObjectOffset(
		sideLabel1 = new Text(hgeResMan, 400.f, 30.f,
			new SEvent<RoomScene>(this, &RoomScene::ChangeSide1)),
		20.f, 10.f);
	mainPanel->placeGuiObjectOffset(
		sideLabel2 = new Text(hgeResMan, 400.f, 30.f,
			new SEvent<RoomScene>(this, &RoomScene::ChangeSide2)),
		20.f, 230.f
		);
	sideLabel1->printf("Side1");
	sideLabel2->printf("Side2");

	for (int i = 0; i < MAX_PLAYER_COUNT / 2; i++)
	{
		boards[i] = new DisplayBoard(hgeResMan, 520.f, 30.f,
			new SEvent<RoomScene>(this, &RoomScene::ChangeType));
		mainPanel->placeGuiObjectOffset(boards[i], 20.f, 50.f + 35.f * i);
	}

	for (int i = MAX_PLAYER_COUNT / 2; i < MAX_PLAYER_COUNT; i++)
	{
		boards[i] = new DisplayBoard(hgeResMan, 520.f, 30.f,
			new SEvent<RoomScene>(this, &RoomScene::ChangeType));
		mainPanel->placeGuiObjectOffset(boards[i], 20.f, 90.f + 35.f * i);
	}

	if (netIO->IsServer())
	{
		int id = netIO->AddPlayer( userName, netIO->node->GetMyBoundAddress(), 0);
#ifdef _DEBUG
		assert( id == 0 );
#endif
		boards[0]->EnableBoard(id);
		netIO->SetMeId(0);
		netIO->SetPlayerReady( 0, true );
		netIO->SetPlayerConfirm( 0 );
		menu->placeGuiObjectOffset(
			start = new Button(hgeResMan, 0.5f, "Start", 
				new SEvent<RoomScene>(this, &RoomScene::Start)
		), 40.f, 170.f );
	}
	else
	{
		BitStream bso;
		bso.Write((uchar)ID_INFO);
		netIO->sendToBattleServer(&bso);
		menu->placeGuiObjectOffset(
			ready = new Button(hgeResMan, 0.5f, "Ready", 
				new SEvent<RoomScene>(this, &RoomScene::Ready)
			), 40.f, 170.f );
	}

	menu->placeGuiObjectOffset(
		back = new Button(hgeResMan, 0.5f, "Back",
			new SEvent<RoomScene>(this, &RoomScene::Back)
		), 
		40.0f, 220.0f);

	menu->addToGui(sceneManager);
	mainPanel->addToGui(sceneManager);
	return true;
}

void RoomScene::Start()
{
	if ( !gameStart )
	{
		if ( netIO->readyCount == netIO->GetPlayerCount() )
		{
			gameStart = true;
			start->SetTitle("Starting...");
			netIO->Lock();
			BitStream bso;
			bso.Write((uchar)ID_START);
			netIO->SerializePlayerInfo(&bso, false);
			netIO->broadcastExcludePlayer(&bso, -1);
		}
	}
}

void RoomScene::Back()
{
	netIO->reset();
	netIO->CloseConnection();
	sceneManager->setNextScene(new MainMenuScene());
	Leave();
}


void RoomScene::EnterBattle()
{
	sceneManager->setNextScene(new BattleScene());
	Leave();
}

void RoomScene::Ready()
{
	BitStream bso;
	uchar id = netIO->getMeId();
	if (!netIO->IsServer())
	{
		RToggleReady rtr;
		rtr.id = id;
		rtr.Serialize(&bso);
		netIO->sendToBattleServer(&bso);
	}
}

void RoomScene::ChangeType()
{
	if (netIO->IsServer() && netIO->IsLocked()) return;
	for (int i = 0; i < MAX_PLAYER_COUNT; i++) 
	{
		if (boards[i]->status && boards[i]->chosen && boards[i]->infoId == netIO->getMeId())
		{
			TChangeType ct;
			BitStream bso;
			if (netIO->IsServer())
			{
				int newType = netIO->ChangePlayerType(boards[i]->infoId);
				ct.id = boards[i]->infoId;
				ct.type = newType;
				ct.Serialize(&bso);
				netIO->broadcastExcludePlayer(&bso, -1);
			}
			else
			{
				RChangeType rct;
				rct.id = (uchar)boards[i]->infoId;
				rct.Serialize(&bso);
				netIO->sendToBattleServer(&bso);
			}
			break;
		}
	}
}

void RoomScene::ChangeSide1()
{
	int id = netIO->getMeId();
	BitStream bso;
	if (netIO->GetPlayerInfo()[id].side) {
		if (netIO->IsServer())
		{
			if (netIO->IsLocked()) return;
			ChangePlayerSide(id, false);
			TChangeSide tcs;
			tcs.id = id;
			tcs.boardId = netIO->GetPlayerInfo()[tcs.id].boardId;
			tcs.Serialize(&bso);
			netIO->broadcastExcludePlayer(&bso, -1);
		}
		else
		{
			RChangeSide rcs;
			rcs.id = id; rcs.side = false;
			rcs.Serialize(&bso);
			netIO->sendToBattleServer(&bso);
		}
	}
}

void RoomScene::ChangeSide2()
{
	int id = netIO->getMeId();
	BitStream bso;
	if (!netIO->GetPlayerInfo()[id].side) {
		if (netIO->IsServer())
		{
			if (netIO->IsLocked()) return;
			ChangePlayerSide(id, true);
			TChangeSide tcs;
			tcs.id = id;
			tcs.boardId = netIO->GetPlayerInfo()[tcs.id].boardId;
			tcs.Serialize(&bso);
			netIO->broadcastExcludePlayer(&bso, -1);
		}
		else
		{
			RChangeSide rcs;
			rcs.id = id; rcs.side = true;
			rcs.Serialize(&bso);
			netIO->sendToBattleServer(&bso);
		}
	}
}

bool RoomScene::update(float dt)
{
	Packet* p;
	BitStream bso;
	if (netIO->IsServer())
	{
		Join iJoin;
		PlayerDescriptor* psd;
		if (p = netIO->m_recv())
		{
			switch (p->data[0])
			{
			case ID_NEW_INCOMING_CONNECTION:
				if ( netIO->IsLocked() || netIO->GetPlayerCount() >= MAX_PLAYER_COUNT)
				{
					netIO->node->CloseConnection(p->systemAddress, true);
				}
				break;
			case ID_DISCONNECTION_NOTIFICATION:
				{
					Exit ex;
					uchar id = netIO->findPlayerBySystemAddress(p->systemAddress);
					if (id == 255) {
						cout << "Lost Connection from unknown server.\n";
						break;
					}
					netIO->node->CloseConnection(p->systemAddress, false);
					cout << "playerid: " << (int)id << endl;
					if (netIO->IsLocked()) netIO->SetPlayerLost(id);
					netIO->SetPlayerReady(id, false);
					netIO->SetPlayerCount(netIO->GetPlayerCount() - 1);
					netIO->GetPlayerInfo()[id].available = false;
					boards[netIO->GetPlayerInfo()[id].boardId]->status = false;

					ex.id = id;
					ex.Serialize(&bso);
					netIO->broadcastExcludePlayer(&bso, -1);
#ifdef _DEBUG
					cout << "client: " << p->systemAddress.ToString() << " exit normally.\n";
#endif
					
				}
				break;
			case ID_CONNECTION_LOST:
				{
					Exit ex;
					uchar id = netIO->findPlayerBySystemAddress(p->systemAddress);
					if (id == 255) {
						cout << "Lost connection from unknown server.\n";
						break;
					}

					netIO->node->CloseConnection(p->systemAddress, false);
					if (netIO->IsLocked()) netIO->SetPlayerLost( id );
					netIO->SetPlayerReady(id, false);
					netIO->SetPlayerCount(netIO->GetPlayerCount() - 1);
					netIO->GetPlayerInfo()[id].available = false;
					boards[netIO->GetPlayerInfo()[id].boardId]->status = false;

					ex.id = id;
					ex.Serialize(&bso);
					netIO->broadcastExcludePlayer(&bso, -1);
#ifdef _DEBUG
					cout << "client: " << p->systemAddress.ToString() << " exit with exception.\n";
#endif
				}
				break;
			case ID_JOIN:
				{
					if (netIO->IsLocked())
					{
						bso.Write((uchar)ID_ALREADY_LOCK);
						netIO->SendToSpecifiedAddress(&bso, p->systemAddress);
						netIO->node->CloseConnection(p->systemAddress, false);
						break;
					}

					if (netIO->GetPlayerCount() >= MAX_PLAYER_COUNT)
					{
						bso.Write((uchar)ID_FULL);
						netIO->SendToSpecifiedAddress(&bso, p->systemAddress);
						netIO->node->CloseConnection(p->systemAddress, false);
						break;
					}

					BitStream bsi(p->data, p->length, false);
					psd = netIO->GetPlayerInfo();
					iJoin.Deserialize(&bsi);
					cout << iJoin.userName << " : " << p->systemAddress.ToString() << " joined\n";
					int i;
					for (i = 0; i < 10; i++)
					{
						if (psd[i].available && !strcmp(iJoin.userName, psd[i].userName)) 
						{
							cout << "send.\n";
							bso.Write((uchar)ID_NAME_DUPLICATE);
							netIO->SendToSpecifiedAddress(&bso, p->systemAddress);
							netIO->node->CloseConnection(p->systemAddress, true);
							break;
						}
					}
					if ( i < 10 ) break;
					
					int id;
					for (int i = 0; i < MAX_PLAYER_COUNT; i++)
					{
						if (!boards[i]->status) {
							boards[i]->status = true;
							id = netIO->AddPlayer(iJoin.userName, p->systemAddress, i);
							boards[i]->infoId = id;
							break;
						}
					}

					bso.Write((uchar)ID_JOIN_ACK);
					bso.Write((uchar)id);
					netIO->SendToSpecifiedAddress(&bso, p->systemAddress);

					bso.Reset();
					netIO->SerializeSinglePlayerInfo(&bso, id);
					netIO->broadcastExcludeAddress(&bso, p->systemAddress);
					cout << "broadcast exclude " << p->systemAddress.ToString() << endl;
				}
				break;
			case ID_INFO:
				netIO->SerializePlayerInfo(&bso);
				netIO->SendToSpecifiedAddress(&bso, p->systemAddress);
				break;
			case ID_CHANGE_TYPE:
				{
					if (netIO->IsLocked()) break;
					BitStream bsi(p->data, p->length, false);
					RChangeType rct;
					rct.Deserialize(&bsi);
					int newType = netIO->ChangePlayerType(rct.id);
					TChangeType tct;
					tct.id = rct.id;
					tct.type = newType;
					tct.Serialize(&bso);
					netIO->broadcastExcludePlayer(&bso, -1);
					netIO->SetPlayerReady(tct.id, false);
				}
				break;
			case ID_CHANGE_SIDE:
				{
					if (netIO->IsLocked()) break;
					BitStream bsi(p->data, p->length, false);
					RChangeSide rcs;
					TChangeSide tcs;
					rcs.Deserialize(&bsi);
					bool ret = ChangePlayerSide(rcs.id, rcs.side);
					tcs.id = rcs.id;
					tcs.boardId = netIO->GetPlayerInfo()[tcs.id].boardId;
					tcs.Serialize(&bso);
					if ( ret )
					{
						netIO->broadcastExcludePlayer(&bso, -1);
						netIO->SetPlayerReady(tcs.id, false);
					}
					else netIO->broadcastExcludeAddress(&bso, p->systemAddress);
				}
				break;
			case ID_READY:
				{
					if (netIO->IsLocked()) break;
					BitStream bsi(p->data, p->length, false);
					RToggleReady rtr;
					TToggleReady ttr;
					rtr.Deserialize(&bsi);
					ttr.id = rtr.id;
					ttr.ready = netIO->ToggleReadyState(ttr.id);
					ttr.Serialize(&bso);
					netIO->broadcastExcludePlayer(&bso, -1);
				}
				break;
			case ID_START:
				{
					BitStream bsi(p->data, p->length, false);
					uchar whead, id;
					bsi.Read(whead);
					bsi.Read(id);
					netIO->SetPlayerConfirm(id);
					cout << "player " << netIO->GetPlayerInfo()[id].userName << " confirmed.\n";
				}
				break;
			default:
				cout << "can't handle packet with head " << (int)p->data[0] << endl;
			}
			netIO->m_Deallocate(p);
		}
		if ( gameStart )
		{
			if ( !entered && netIO->confirmCount == netIO->GetPlayerCount() )
			{
				entered = true;
				BitStream bsof;
				bsof.Write((uchar)ID_ENTER);
				netIO->broadcastExcludePlayer(&bsof, -1);
				EnterBattle();
			}
		}
	}
	else {
		Join iJoin;
		bool needToBack = false;
		bool needToStartBattle = false;
		if (p = netIO->m_recv())
		{
			switch (p->data[0])
			{
			case ID_DISCONNECTION_NOTIFICATION:
				cout << "server exit normally.\n";
				needToBack = true;
				break;
			case ID_CONNECTION_LOST:
				cout << "server exit with exception.\n";
				needToBack = true;
				break;
			case ID_START:
				{
					BitStream bsi(p->data, p->length, false);
					uchar whead;
					bsi.Read(whead);
					netIO->DeSerializationPlayerInfo(&bsi, false);
					bso.Write((uchar)ID_START);
					bso.Write(netIO->getMeId());
					netIO->sendToBattleServer(&bso);
				}
				break;
			case ID_ENTER:
				needToStartBattle = true;
				break;
			case ID_INFO_INCREMENT:
				{
					BitStream bsi(p->data, p->length, false);
					int id = netIO->DeserializeSinglePlayerInfo(&bsi);
					netIO->GetPlayerInfo()[id].flyObjectType = 1;
					netIO->GetPlayerInfo()[id].ready = false;
					boards[netIO->GetPlayerInfo()[id].boardId]->EnableBoard(id);
				}
				break;
			case ID_INFO:
				{
					BitStream bsi(p->data, p->length, false);
					netIO->DeSerializationPlayerInfo(&bsi);
					PlayerDescriptor* psd = netIO->GetPlayerInfo();
					for ( int i = 0; i < MAX_PLAYER_COUNT; i++ )
					{
						if (psd[i].available) {
							boards[psd[i].boardId]->EnableBoard(i);
						}
					}
				}
				break;
			case ID_CHANGE_TYPE:
				{
					TChangeType tct;
					BitStream bsi(p->data, p->length, false);
					tct.Deserialize(&bsi);
					netIO->GetPlayerInfo()[tct.id].flyObjectType = tct.type;

					if (tct.id) netIO->SetPlayerReady( tct.id, false );
					if (tct.id == netIO->getMeId()) 	ready->SetTitle("Ready");
				}
				break;
			case ID_CHANGE_SIDE:
				{
					TChangeSide tcs;
					BitStream bsi(p->data, p->length, false);
					tcs.Deserialize(&bsi);
					if (tcs.boardId < MAX_PLAYER_COUNT / 2)
						netIO->GetPlayerInfo()[tcs.id].side = 0;
					else netIO->GetPlayerInfo()[tcs.id].side = 1;
					boards[netIO->GetPlayerInfo()[tcs.id].boardId]->status = false;
					netIO->GetPlayerInfo()[tcs.id].boardId = tcs.boardId;
					boards[tcs.boardId]->EnableBoard(tcs.id);

					if (tcs.id) netIO->SetPlayerReady( tcs.id, false );
					if (tcs.id == netIO->getMeId()) 	ready->SetTitle("Ready");
				}
				break;
			case ID_READY:
				{
					BitStream bsi( p->data, p->length, false );
					TToggleReady ttr;
					ttr.Deserialize(&bsi);
					if (ttr.id) netIO->SetPlayerReady(ttr.id, ttr.ready);
					if (ttr.id == netIO->getMeId()) {
						if (ttr.ready) ready->SetTitle("Cancel Ready");
						else ready->SetTitle("Ready");
					}
				}
				break;
			case ID_EXIT:
				{
					Exit ex;
					BitStream bsi( p->data, p->length, false );
					ex.Deserialize(&bsi);
					netIO->SetPlayerReady( ex.id, false );
					netIO->SetPlayerCount(netIO->GetPlayerCount() - 1);
					netIO->GetPlayerInfo()[ex.id].available = false;
					boards[netIO->GetPlayerInfo()[ex.id].boardId]->status = false;
				}
				break;
			default:
				cout << "can't handle packet with head " << (int)p->data[0] << endl;
			}
			netIO->m_Deallocate(p);
			if (needToBack) Back();
			if ( !entered && needToStartBattle ) {
				entered = true;
				EnterBattle();
			}
		}
	}
	return false;
}

bool RoomScene::render(float dt)
{
	hge->Gfx_Clear(0xffffffff);
	acc->Render();
	gui->Render();
	return false;
}

void RoomScene::Enter() 
{
	gui->Enter();
}

void RoomScene::Leave() 
{
	menu->SetTransfromationTween( -200.f, 0.f, 0.25, 
		new SEvent<RoomScene>(this, &RoomScene::leaveCounter) );
	mainPanel->SetTransfromationTween( 210.f, -500.f, 0.25,
		new SEvent<RoomScene>(this, &RoomScene::leaveCounter) );
}

bool RoomScene::ChangePlayerSide(uchar id, bool side)
{
	netIO->GetPlayerInfo()[id].side = side ? 1 : 0;
	if (side ^ (netIO->GetPlayerInfo()[id].boardId >= MAX_PLAYER_COUNT / 2))
	{
		bool found = false;
		if ( !side ) 
		{
			for ( int i = 0; i < MAX_PLAYER_COUNT / 2; i++ ) 
			{
				if ( !boards[i]->status )
				{
					found = true;
					boards[netIO->GetPlayerInfo()[id].boardId]->status = false;
					netIO->GetPlayerInfo()[id].boardId = i;
					boards[i]->EnableBoard(id);
					break;
				}
			}
		}
		else {
			for ( int i = MAX_PLAYER_COUNT / 2; i < MAX_PLAYER_COUNT; i++ )
			{
				if ( !boards[i]->status )
				{
					found = true;
					boards[netIO->GetPlayerInfo()[id].boardId]->status = false;
					netIO->GetPlayerInfo()[id].boardId = i;
					boards[i]->EnableBoard(id);
					break;
				}
			}
		}
		if ( found ) return true; else return false;
	}
	else return false;
}

void RoomScene::DeSerialization(BitStream* bsi)
{
}
